﻿using Microsoft.Extensions.Localization;
using PHMEE.Localization;
using PHMEE.ToolKits;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Domain.Services;

namespace PHMEE.Managers.BaseInfo.Vessels
{
    public class VesselManager : DomainService, IVesselManager
    {
        private readonly IStringLocalizer<PHMEEResource> L;
        private readonly IVesselRepository vesselRepository;
        private readonly IFleetManager fleetManager;
        private readonly IPointRepository pointRepository;
        private readonly string Language;

        public VesselManager(IStringLocalizer<PHMEEResource> L, IVesselRepository vesselRepository, IFleetManager fleetManager, IPointRepository pointRepository)
        {
            this.L = L;
            this.vesselRepository = vesselRepository;
            this.fleetManager = fleetManager;
            this.pointRepository = pointRepository;
            this.Language = CultureInfo.CurrentCulture.TwoLetterISOLanguageName;
        }

        public async Task<Vessel> CreateVessel(VesselEnum vesselType, Guid fleetId, string name, string nameen, string imo)
        {
            var (expression, localizedName) = GetVesselExpression(name, nameen, fleetId);

            var existingVessel = await vesselRepository.FirstOrDefaultAsync(expression);

            if (existingVessel != null)
            {
                throw new UserFriendlyException(L[PHMEEDomainErrorCodes.VesselNameAlreadyExist], PHMEEDomainErrorCodes.VesselNameAlreadyExist).WithData("Name", localizedName);
            }
            var id = GuidGenerator.Create();
            Vessel vessel = null;
            switch (vesselType)
            {
                case VesselEnum.Bulker:
                    vessel = new Bulker(id, fleetId, name, nameen, imo);
                    break;
                case VesselEnum.Container:
                    vessel = new Container(id, fleetId, name, nameen, imo);
                    break;
                case VesselEnum.Tanker:
                    vessel = new Tanker(id, fleetId, name, nameen, imo);
                    break;
            }
            return vessel;
        }

        public (Expression<Func<Vessel, bool>> Expression, string LocalizedName) GetVesselExpression(string name, string nameen, Guid fleetId)
        {
            Expression<Func<Vessel, bool>> expression = item => item.FleetId == fleetId;

            string localizedName = string.Empty;

            switch (Language)
            {
                case PhmeeConst.LanguageEn:

                    localizedName = nameen;

                    expression = expression.And(item => item.NameEn == nameen);

                    break;
                default:

                    localizedName = name;

                    expression = expression.And(item => item.Name == name);

                    break;
            }

            return (expression, localizedName);
        }

        /// <summary>
        /// 根据船舶获取层级关系
        /// </summary>
        /// <param name="vesselId"></param>
        /// <returns></returns>
        public async Task<Vessel> GetVesselRelation(Guid vesselId)
        {
            var queryVessel = await vesselRepository.PhmeeGetQuery();

            queryVessel = queryVessel.Where(item => item.Id == vesselId);

            var item = queryVessel.FirstOrDefault();

            return item;
        }


        /// <summary>
        /// 根据机组获取层级关系
        /// </summary>
        /// <param name="unitId"></param>
        /// <returns></returns>
        public async Task<(Vessel, Unit)> GetUnitRelation(Guid unitId)
        {
            var queryVessel = await vesselRepository.PhmeeGetQuery();

            var query = queryVessel.SelectMany(vessel => vessel.Units.Select(unit => new { Vessel = vessel, Unit = unit }));

            query = query.Where(m => m.Unit.Id == unitId);

            var item = query.FirstOrDefault();

            return (item?.Vessel, item?.Unit);
        }

        /// <summary>
        /// 根据设备获取层级关系
        /// </summary>
        /// <param name="equipmentId"></param>
        /// <returns></returns>
        public async Task<(Vessel, Unit, Equipment)> GetEquipmentRelation(Guid equipmentId)
        {
            var queryVessel = await vesselRepository.PhmeeGetQuery();

            var query = queryVessel.SelectMany(vessel => vessel.Units
                                         .SelectMany(unit => unit.Equipments
                                              .Select(equipment => new { Vessel = vessel, Unit = unit, Equipment = equipment })));

            query = query.Where(m => m.Equipment.Id == equipmentId);

            var item = query.FirstOrDefault();

            return (item?.Vessel, item?.Unit, item?.Equipment);
        }

        /// <summary>
        /// 根据组件获取层级关系
        /// </summary>
        /// <param name="componentId"></param>
        /// <returns></returns>
        public async Task<(Vessel, Unit, Equipment, Component)> GetComponentRelation(Guid componentId)
        {
            var queryVessel = await vesselRepository.PhmeeGetQuery();

            var query = queryVessel.SelectMany(vessel => vessel.Units
                                         .SelectMany(unit => unit.Equipments
                                               .SelectMany(equipment => equipment.Components
                                                     .Select(component => new { Vessel = vessel, Unit = unit, Equipment = equipment, Component = component }))));

            query = query.Where(m => m.Component.Id == componentId);

            var item = query.FirstOrDefault();

            return (item?.Vessel, item?.Unit, item?.Equipment, item?.Component);
        }

        /// <summary>
        /// 获取基础信息层级结构
        /// </summary>
        /// <returns></returns>
        public async Task<List<VesselTree>> GetBaseInfoTree(bool includePoints = false)
        {
            List<VesselTree> trees = new List<VesselTree>();
            var queryVessel = await vesselRepository.WithDetailsAsync();
            var vessels = await AsyncExecuter.ToListAsync(queryVessel);
            var points = await pointRepository.PhmeeGetAll();
            foreach (var vessel in vessels)
            {
                var node = GenerateTree(vessel, points, includePoints);
                trees.Add(node);
            }
            return trees;
        }

        private static VesselTree GenerateTree(Vessel vessel, List<Point> points, bool includePoints)
        {
            var vesselNode = new VesselTree
            {
                Id = vessel.Id.ToString(),
                Name = vessel.DisplayName,
                Type = nameof(Vessel),
            };

            if (includePoints)
            {
                var vesselPoints = points.FindAll(p => p.OwnerId == vessel.Id && p.OwnerType == nameof(Vessel)).OrderBy(item => item.OwnerType).ThenBy(item => item.OwnerId);
                foreach (var point in vesselPoints)
                {
                    vesselNode.Points.Add(new VesselTree
                    {
                        Id = point.Id.ToString(),
                        Name = point.DisplayName,
                        Type = nameof(Point),
                        Category = point.Category.ToString(),
                    });
                }
            }

            foreach (var unit in vessel.Units.OrderBy(item => item.Type).ThenBy(item => item.Number))
            {
                var unitNode = GenerateTree(unit, points, includePoints);
                vesselNode.Children.Add(unitNode);
            }
            return vesselNode;
        }

        private static VesselTree GenerateTree(Unit unit, List<Point> points, bool includePoints)
        {
            var unitNode = new VesselTree
            {
                Id = unit.Id.ToString(),
                Name = unit.DisplayName,
                Type = nameof(Unit),
            };

            if (includePoints)
            {
                var unitPoints = points.FindAll(p => p.OwnerId == unit.Id && p.OwnerType == nameof(Unit)).OrderBy(item => item.OwnerType).ThenBy(item => item.OwnerId);
                foreach (var point in unitPoints)
                {
                    unitNode.Points.Add(new VesselTree
                    {
                        Id = point.Id.ToString(),
                        Name = point.DisplayName,
                        Type = nameof(Point),
                        Category = point.Category.ToString(),
                    });
                }
            }

            foreach (var equipment in unit.Equipments.OrderBy(item => item.Type).ThenBy(item => item.Number))
            {
                var equipmentNode = GenerateTree(equipment, points, includePoints);
                unitNode.Children.Add(equipmentNode);
            }

            return unitNode;
        }

        private static VesselTree GenerateTree(Equipment equipment, List<Point> points, bool includePoints)
        {
            var equipmentNode = new VesselTree
            {
                Id = equipment.Id.ToString(),
                Name = equipment.DisplayName,
                Type = nameof(Equipment),
            };

            if (includePoints)
            {
                var equipmentPoints = points.FindAll(p => p.OwnerId == equipment.Id && p.OwnerType == nameof(Equipment)).OrderBy(item => item.OwnerType).ThenBy(item => item.OwnerId);
                foreach (var point in equipmentPoints)
                {
                    equipmentNode.Points.Add(new VesselTree
                    {
                        Id = point.Id.ToString(),
                        Name = point.DisplayName,
                        Type = nameof(Point),
                        Category = point.Category.ToString(),
                    });
                }
            }

            foreach (var component in equipment.Components.OrderBy(item => item.Type).ThenBy(item => item.Number))
            {
                var componentNode = GenerateTree(component, points, includePoints);
                equipmentNode.Children.Add(componentNode);
            }

            return equipmentNode;
        }

        private static VesselTree GenerateTree(Component component, List<Point> points, bool includePoints)
        {
            var componentNode = new VesselTree
            {
                Id = component.Id.ToString(),
                Name = component.DisplayName,
                Type = nameof(Component),
            };

            if (includePoints)
            {
                var componentPoints = points.FindAll(p => p.OwnerId == component.Id && p.OwnerType == nameof(Component)).OrderBy(item => item.OwnerType).ThenBy(item => item.OwnerId);
                foreach (var point in componentPoints)
                {
                    componentNode.Points.Add(new VesselTree
                    {
                        Id = point.Id.ToString(),
                        Name = point.DisplayName,
                        Type = nameof(Point),
                        Category = point.Category.ToString(),
                    });
                }
            }

            return componentNode;
        }

        public Dictionary<int, object> GetVesselTypes()
        {
            return PHMEECoreHelper.PhmeeGetEnumKeyDescriptions<VesselEnum>();
        }

        public Dictionary<int, object> GetVesselFlowMeterModes()
        {
            return PHMEECoreHelper.PhmeeGetEnumKeyDescriptions<FlowMeterModeEnum>();
        }

        public Dictionary<int, object> GetUnitTypes()
        {
            return PHMEECoreHelper.PhmeeGetEnumKeyDescriptions<UnitEnum>();
        }

        public Dictionary<int, object> GetEquipmentTypes()
        {
            return PHMEECoreHelper.PhmeeGetEnumKeyDescriptions<EquipmentEnum>();
        }

        public Dictionary<int, object> GetEquipmentRotationModes()
        {
            return PHMEECoreHelper.PhmeeGetEnumKeyDescriptions<RotationMode>();
        }
        public Dictionary<int, object> GetEquipmentConnectModes()
        {
            return PHMEECoreHelper.PhmeeGetEnumKeyDescriptions<ConnectMode>();
        }
        public Dictionary<int, object> GetEquipmentSupportFlexibilityModes()
        {
            return PHMEECoreHelper.PhmeeGetEnumKeyDescriptions<SupportFlexibilityMode>();
        }
        public Dictionary<int, object> GetEquipmentBearingTypes()
        {
            return PHMEECoreHelper.PhmeeGetEnumKeyDescriptions<BearingType>();
        }

        public Dictionary<int, object> GetComponentTypes()
        {
            return PHMEECoreHelper.PhmeeGetEnumKeyDescriptions<ComponentEnum>();
        }

        public Dictionary<int, object> GetBaseInfoLevelTags(string ownerType)
        {
            List<BaseInfoTagEnum> filter = null;

            switch (ownerType)
            {
                case nameof(Vessel):
                    filter = Vessel.GetLevelTagOptions();
                    break;
                case nameof(Unit):
                    filter = Unit.GetLevelTagOptions();
                    break;
                case nameof(Equipment):
                    filter = Equipment.GetLevelTagOptions();
                    break;
                case nameof(Component):
                    filter = Component.GetLevelTagOptions();
                    break;
            }

            return PHMEECoreHelper.PhmeeGetEnumKeyDescriptions(filter);
        }

    }
}
